/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

export const basicBlob = '/*\n * Copyright (C) 2016-2017 Apple Inc. All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS\'\' AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR\n * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY\n * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \n */\n\"use strict\";\n\nconst Basic = {};\n\nBasic.NumberApply = function(state)\n{\n    // I\'d call this arguments but we\'re in strict mode.\n    let parameters = this.parameters.map(value => value.evaluate(state));\n    \n    return state.getValue(this.name, parameters.length).apply(state, parameters);\n};\n\nBasic.Variable = function(state)\n{\n    let parameters = this.parameters.map(value => value.evaluate(state));\n    \n    return state.getValue(this.name, parameters.length).leftApply(state, parameters);\n}\n\nBasic.Const = function(state)\n{\n    return this.value;\n}\n\nBasic.NumberPow = function(state)\n{\n    return Math.pow(this.left.evaluate(state), this.right.evaluate(state));\n}\n\nBasic.NumberMul = function(state)\n{\n    return this.left.evaluate(state) * this.right.evaluate(state);\n}\n\nBasic.NumberDiv = function(state)\n{\n    return this.left.evaluate(state) / this.right.evaluate(state);\n}\n\nBasic.NumberNeg = function(state)\n{\n    return -this.term.evaluate(state);\n}\n\nBasic.NumberAdd = function(state)\n{\n    return this.left.evaluate(state) + this.right.evaluate(state);\n}\n\nBasic.NumberSub = function(state)\n{\n    return this.left.evaluate(state) - this.right.evaluate(state);\n}\n\nBasic.StringVar = function(state)\n{\n    let value = state.stringValues.get(this.name);\n    if (value == null)\n        state.abort(\"Could not find string variable \" + this.name);\n    return value;\n}\n\nBasic.Equals = function(state)\n{\n    return this.left.evaluate(state) == this.right.evaluate(state);\n}\n\nBasic.NotEquals = function(state)\n{\n    return this.left.evaluate(state) != this.right.evaluate(state);\n}\n\nBasic.LessThan = function(state)\n{\n    return this.left.evaluate(state) < this.right.evaluate(state);\n}\n\nBasic.GreaterThan = function(state)\n{\n    return this.left.evaluate(state) > this.right.evaluate(state);\n}\n\nBasic.LessEqual = function(state)\n{\n    return this.left.evaluate(state) <= this.right.evaluate(state);\n}\n\nBasic.GreaterEqual = function(state)\n{\n    return this.left.evaluate(state) >= this.right.evaluate(state);\n}\n\nBasic.GoTo = function*(state)\n{\n    state.nextLineNumber = this.target;\n}\n\nBasic.GoSub = function*(state)\n{\n    state.subStack.push(state.nextLineNumber);\n    state.nextLineNumber = this.target;\n}\n\nBasic.Def = function*(state)\n{\n    state.validate(!state.values.has(this.name), \"Cannot redefine function\");\n    state.values.set(this.name, new NumberFunction(this.parameters, this.expression));\n}\n\nBasic.Let = function*(state)\n{\n    this.variable.evaluate(state).assign(this.expression.evaluate(state));\n}\n\nBasic.If = function*(state)\n{\n    if (this.condition.evaluate(state))\n        state.nextLineNumber = this.target;\n}\n\nBasic.Return = function*(state)\n{\n    this.validate(state.subStack.length, \"Not in a subroutine\");\n    this.nextLineNumber = state.subStack.pop();\n}\n\nBasic.Stop = function*(state)\n{\n    state.nextLineNumber = null;\n}\n\nBasic.On = function*(state)\n{\n    let index = this.expression.evaluate(state);\n    if (!(index >= 1) || !(index <= this.targets.length))\n        state.abort(\"Index out of bounds: \" + index);\n    this.nextLineNumber = this.targets[Math.floor(index)];\n}\n\nBasic.For = function*(state)\n{\n    let sideState = state.getSideState(this);\n    sideState.variable = state.getValue(this.variable, 0).leftApply(state, []);\n    sideState.initialValue = this.initial.evaluate(state);\n    sideState.limitValue = this.limit.evaluate(state);\n    sideState.stepValue = this.step.evaluate(state);\n    sideState.variable.assign(sideState.initialValue);\n    sideState.shouldStop = function() {\n        return (sideState.variable.value - sideState.limitValue) * Math.sign(sideState.stepValue) > 0;\n    };\n    if (sideState.shouldStop())\n        this.nextLineNumber = this.target.lineNumber + 1;\n}\n\nBasic.Next = function*(state)\n{\n    let sideState = state.getSideState(this.target);\n    sideState.variable.assign(sideState.variable.value + sideState.stepValue);\n    if (sideState.shouldStop())\n        return;\n    state.nextLineNumber = this.target.lineNumber + 1;\n}\n\nBasic.Next.isBlockEnd = true;\n\nBasic.Print = function*(state)\n{\n    let string = \"\";\n    for (let item of this.items) {\n        switch (item.kind) {\n        case \"comma\":\n            while (string.length % 14)\n                string += \" \";\n            break;\n        case \"tab\": {\n            let value = item.value.evaluate(state);\n            value = Math.max(Math.round(value), 1);\n            while (string.length % value)\n                string += \" \";\n            break;\n        }\n        case \"string\":\n        case \"number\":\n            string += item.value.evaluate(state);\n            break;\n        default:\n            throw new Error(\"Bad item kind: \" + item.kind);\n        }\n    }\n    \n    yield {kind: \"output\", string};\n}\n\nBasic.Input = function*(state)\n{\n    let results = yield {kind: \"input\", numItems: this.items.length};\n    state.validate(results != null && results.length == this.items.length, \"Input did not get the right number of items\");\n    for (let i = 0; i < results.length; ++i)\n        this.items[i].evaluate(state).assign(results[i]);\n}\n\nBasic.Read = function*(state)\n{\n    for (let item of this.items) {\n        state.validate(state.dataIndex < state.program.data.length, \"Attempting to read past the end of data\");\n        item.assign(state.program.data[state.dataIndex++]);\n    }\n}\n\nBasic.Restore = function*(state)\n{\n    state.dataIndex = 0;\n}\n\nBasic.Dim = function*(state)\n{\n    for (let item of this.items) {\n        state.validate(!state.values.has(item.name), \"Variable \" + item.name + \" already exists\");\n        state.validate(item.bounds.length, \"Dim statement is for arrays\");\n        state.values.set(item.name, new NumberArray(item.bounds.map(bound => bound + 1)));\n    }\n}\n\nBasic.Randomize = function*(state)\n{\n    state.rng = createRNGWithRandomSeed();\n}\n\nBasic.End = function*(state)\n{\n    state.nextLineNumber = null;\n}\n\nBasic.End.isBlockEnd = true;\n\nBasic.Program = function* programGenerator(state)\n{\n    state.validate(state.program == this, \"State must match program\");\n    let maxLineNumber = Math.max(...this.statements.keys());\n    while (state.nextLineNumber != null) {\n        state.validate(state.nextLineNumber <= maxLineNumber, \"Went out of bounds of the program\");\n        let statement = this.statements.get(state.nextLineNumber++);\n        if (statement == null || statement.process == null)\n            continue;\n        state.statement = statement;\n        yield* statement.process(state);\n    }\n}\n\n';
